﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mono.Cecil;

namespace LinFu.AOP.CecilExtensions
{
	public static class AssemblyDefinitionExtensions
	{
		public static void WeaveWith(this AssemblyDefinition assembly, IWeaver<TypeDefinition> typeWeaver)
		{
			if (assembly == null)
				throw new ArgumentNullException("assembly");

			if (typeWeaver == null)
				throw new ArgumentNullException("typeWeaver");

			ModuleDefinition module = assembly.MainModule;
			typeWeaver.ImportReferences(module);

			List<TypeDefinition> typesToWeave = new List<TypeDefinition>();
			for (int n = 0; n < module.Types.Count; n++)
			{
				TypeDefinition t = module.Types[n];
				if (t == null || t.Name == "<Module>" || !typeWeaver.ShouldWeave(t)) continue;
				bool attributeSet = false;

				foreach (CustomAttribute attr in t.CustomAttributes)
				{
					//Console.WriteLine(attr.Constructor.DeclaringType.Name);
					if (attr.Constructor.DeclaringType.Name.Contains("AOPEnabledAttribute"))
					{
						attributeSet = true;						
						break;
					}
				}

				if (attributeSet)
					typesToWeave.Add(t);
			}
			//var matches = (from TypeDefinition t in module.Types
			//               where t != null && t.Name != "<Module>"
			//               && typeWeaver.ShouldWeave(t)
			//               select t).ToList();
			
			//matches.ForEach(match => typeWeaver.Weave(match));
			typesToWeave.ForEach(match => typeWeaver.Weave(match));
		}

		public static void WeaveWith(this AssemblyDefinition assembly,
			Predicate<TypeDefinition> typeFilter, IMethodWeaver methodWeaver)
		{
			if (assembly == null)
				throw new ArgumentNullException("assembly");

			if (typeFilter == null)
				throw new ArgumentNullException("typeFilter");

			if (methodWeaver == null)
				throw new ArgumentNullException("methodWeaver");

			ModuleDefinition module = assembly.MainModule;
			var matches = (from TypeDefinition t in module.Types
						   where t != null && t.Name != "<Module>"
						   && typeFilter(t)
						   select t).ToList();

			matches.ForEach(type => type.WeaveWith(methodWeaver));
		}
		public static void WeaveWith(this AssemblyDefinition assembly, IMethodWeaver methodWeaver)
		{
			if (assembly == null)
				throw new ArgumentNullException("assembly");

			if (methodWeaver == null)
				throw new ArgumentNullException("methodWeaver");

			ModuleDefinition module = assembly.MainModule;
			var matches = (from TypeDefinition t in module.Types
						   where t != null && t.Name != "<Module>"
						   && t.IsClass
						   select t).ToList();

			matches.ForEach(type => type.WeaveWith(methodWeaver));
		}
		public static void Save(this AssemblyDefinition assembly, string filename)
		{
            if (File.Exists(filename + ".bak"))
                File.Delete(filename + ".bak");

            File.Move(filename, filename + ".bak");
            AssemblyFactory.SaveAssembly(assembly, filename);
		}
	}
}
